package com.gid.rcflightlogger;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.os.Vibrator;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;

import com.orm.androrm.Filter;

// Provides a countdown timer, and an diaglog option at the end of the countdown to log flight to db
//
// Some "state machine" mechanics
//	- aircraft and battery selectors
//		. aircraft shows full list
//		. battery selector 1 full list
//		. battery selector 2 empty untill 1 is selected, updated when 1's selection is changed
//
//	- buttons (start and reset)
//		. start = starts the count down, changed to stop, also disables the spinners
//		. stop = stops the countdown
//		. reset = if count down already started, resets timer to full value and stop the count down 
public class FlightTimerActivity extends BaseActivity {
	
	private static final int DEFAULT_TIMER_SECS	= 6*60;
	private static final int TIMER_DELAY_SECS = 5;
	
	private static final String PREF_BEEP = "Beep";
	private static final String PREF_VIBRATE = "Vibrate";
	private static final String PREF_TIMER_PREFIX = "TimerVal";
	
	private static final int DIALOG_ID_AUTOLOG = 0;
	private static final int DIALOG_ID_MANUALLOG = 1;
	
	//db objects
	private List<Aircraft> mAircrafts;	//all aircrafts
	private List<Battery> mBatteries; 	//all batteries
	private Aircraft mEmptyAircraft;
	private Battery mEmptyBattery;
	
	//list for spinners
	private List<Aircraft> mAircraftSpinnerList;
	private List<Battery> mBatterySpinnerList1;
	private List<Battery> mBatterySpinnerList2;

	//adapters for spinners
	private ArrayAdapter<Aircraft> mAircraftSpinnerAdapter;
	private ArrayAdapter<Battery> mBatterySpinnerAdapter1;
	private ArrayAdapter<Battery> mBatterySpinnerAdapter2;
	
	//form objects
	private Spinner mAircraftSpinner;
	private Spinner mBatterySpinner1;
	private Spinner mBatterySpinner2;
	private TimePicker mTimePicker;
	
	private CheckBox mBeep;
	private CheckBox mVibrate;
	
	private Button mStartStop;
	private Button mReset;
	private Button mLog;
	
	//app preferences storage
    private SharedPreferences mPrefs;
    private Editor mPrefsEditor;
	
	//timer data
    boolean mTimerRunning = false;
	private Handler mTimerHandler;
	private int mTimerCountDownInterval = DEFAULT_TIMER_SECS;	//updated on start timer count
	private int mTimerInitCounter = 0;
	private int mTimerMainCounter = 0;	
	long mTimerNextRun = 0;										//in milliseconds, next time this should wake up

	private MediaPlayer mBeepPlayer = null;
	private int mBeepCounter = 0;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.layout_flighttimer);
		
		mCurrentMenuID = R.id.menu_main_flighttimer;
		
		mPrefs = this.getPreferences(Activity.MODE_WORLD_READABLE);
		mPrefsEditor = mPrefs.edit();
	    	
		mEmptyAircraft = new Aircraft();
		mEmptyAircraft.setName("");
	
		mEmptyBattery = new Battery();
		mEmptyBattery.setTag("");
		
		//make spinner lists
		mAircraftSpinnerList = new ArrayList<Aircraft>();
		mBatterySpinnerList1 = new ArrayList<Battery>();
		mBatterySpinnerList2 = new ArrayList<Battery>();

		//prepare adapters 
		mAircraftSpinnerAdapter = new ArrayAdapter<Aircraft>(this, android.R.layout.simple_spinner_item, mAircraftSpinnerList);
		mBatterySpinnerAdapter1 = new ArrayAdapter<Battery>(this, android.R.layout.simple_spinner_item, mBatterySpinnerList1);
		mBatterySpinnerAdapter2 = new ArrayAdapter<Battery>(this, android.R.layout.simple_spinner_item, mBatterySpinnerList2);
				
		//get UI elements
		mAircraftSpinner = (Spinner) findViewById(R.id.flighttimer_spinner_aircraft);
		mAircraftSpinner.setAdapter(mAircraftSpinnerAdapter);
		
		mBatterySpinner1 = (Spinner) findViewById(R.id.flighttimer_spinner_battery1);
		mBatterySpinner1.setAdapter(mBatterySpinnerAdapter1);
		
		mBatterySpinner2 = (Spinner) findViewById(R.id.flighttimer_spinner_battery2);
		mBatterySpinner2.setAdapter(mBatterySpinnerAdapter2);
		
		mTimePicker = (TimePicker) findViewById(R.id.flighttimer_timepicker);
		mTimePicker.setIs24HourView(true);
		setTimePickerValue(DEFAULT_TIMER_SECS);
		
		mBeep = (CheckBox) findViewById(R.id.flighttimer_chbx_beep);
		mBeep.setChecked(mPrefs.getBoolean(PREF_BEEP, true));
		
		mVibrate = (CheckBox) findViewById(R.id.flighttimer_chbx_vibrates);
		mVibrate.setChecked(mPrefs.getBoolean(PREF_VIBRATE, true));
		
		mStartStop = (Button) findViewById(R.id.flighttimer_btn_start);
		mReset = (Button) findViewById(R.id.flighttimer_btn_reset);
		mLog = (Button) findViewById(R.id.flighttimer_btn_log);
		
		mTimerHandler = new Handler();
		
		//set up listeners for spinners
		mAircraftSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				loadTimerPrefValue();
				updateUI();
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				loadTimerPrefValue();
				updateUI();
			}
			
		});
		
		mBatterySpinner1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				//reset spinner 2's content
				mBatterySpinnerList2.clear();
				mBatterySpinnerList2.add(mEmptyBattery);
				if (mBatterySpinner1.getSelectedItemPosition() != 0)
				{
					mBatterySpinnerList2.addAll(mBatteries);
					mBatterySpinnerList2.remove(mBatterySpinner1.getSelectedItem());
				}
				mBatterySpinner2.setSelection(0);
				mBatterySpinnerAdapter2.notifyDataSetChanged();
				loadTimerPrefValue();
				updateUI();
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				loadTimerPrefValue();
				updateUI();
			}			
		});
		
		mBatterySpinner2.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				loadTimerPrefValue();
				updateUI();
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				loadTimerPrefValue();
				updateUI();
			}			
		});
		
		mTimePicker.setOnTimeChangedListener(new OnTimeChangedListener(){

			@Override
			public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
				if (!mTimerRunning)
				{
					mTimerCountDownInterval = 60*hourOfDay + minute;
					mTimerMainCounter = 0;
				}
			}
			
		});
				
		//set up listeners for buttons
		mStartStop.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
	            mTimerRunning = !mTimerRunning;
	            updateUI();
				if (mTimerRunning)
				{
					//start
					mTimerInitCounter = TIMER_DELAY_SECS;
					if (mTimerMainCounter == 0)
					{
						mTimerMainCounter = mTimerCountDownInterval;
						//save prefs
						mPrefsEditor.putBoolean(PREF_BEEP, mBeep.isChecked());
						mPrefsEditor.putBoolean(PREF_VIBRATE, mVibrate.isChecked());
						mPrefsEditor.putInt(buildTimerPrefKey(), mTimerCountDownInterval);
						mPrefsEditor.commit();
					}
					mTimerNextRun = SystemClock.uptimeMillis() + 1000;
					mTimerHandler.removeCallbacks(mTimerTask);
					mTimerHandler.postAtTime(mTimerTask, 1000);
					myUtils.makeToast(FlightTimerActivity.this, "Timer will start in " + TIMER_DELAY_SECS + " seconds. Do not turn screen off!");
				}
				else
				{
					mTimerHandler.removeCallbacks(mTimerTask);
				}
			}
			
		});
		
		mReset.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				mTimerHandler.removeCallbacks(mTimerTask);
				mTimerRunning = false;
				mTimerInitCounter = 0;
				mTimerMainCounter = 0;
				setTimePickerValue(mTimerCountDownInterval);
				updateUI();
			}
		});
		
		mLog.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				showDialog(DIALOG_ID_MANUALLOG);
			}
		});
		
	
		mBeepPlayer = MediaPlayer.create(this, R.raw.longbeep);
		mBeepPlayer.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
				if (mBeepCounter > 0)
					mBeepCounter--;
				
				if (mBeepCounter > 0)
				{
					mBeepPlayer.seekTo(0);
					mBeepPlayer.start();
				}
			}
		});
	}
	
	@Override
	protected void onDestroy() {
		mTimerHandler.removeCallbacks(mTimerTask); //remove posted runnables or else it will crash
		mBeepPlayer.stop();
		mBeepPlayer.release();
		mBeepPlayer = null;
		super.onDestroy();
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		//load data from db
		loadDBData();
		
		final String FIRST_RUN = "FirstRun";
		if (mAircrafts.isEmpty() && mBatteries.isEmpty() && mPrefs.getBoolean(FIRST_RUN, true))
		{
			mPrefsEditor.putBoolean(FIRST_RUN, false);
			mPrefsEditor.commit();
			showDialog(DIALOG_ID_USAGE);
		}
	}
	
	private Runnable mTimerTask = new Runnable() {
		public void run() {
			if (mTimerInitCounter > 0)
			{
				mTimerInitCounter--;
				if (mTimerInitCounter == 0)
				{
					timerAlert(TIMER_ALERT_TICK);
				}
			}
			else
			{
				if (mTimerMainCounter > 0)
					mTimerMainCounter--;
				setTimePickerValue(mTimerMainCounter); //update timer display
				
				if (mTimerMainCounter == 0)
				{
					//done
					timerAlert(TIMER_ALERT_DONE);
					showDialog(DIALOG_ID_AUTOLOG);
					mTimerRunning = false;
					updateUI();
				}
				else if (mTimerMainCounter == 60)
				{
					timerAlert(TIMER_ALERT_SOON);
				}
				else if (mTimerMainCounter%60 == 0)
				{
					timerAlert(TIMER_ALERT_TICK);
				}	
			}
		     
			//finally, schedule another if required
			if (mTimerRunning)
			{
				long current_time = SystemClock.uptimeMillis();
				mTimerNextRun += 1000;
				if (mTimerNextRun <= current_time)
					mTimerNextRun = current_time + 1000; //something is screwed up, try best as we can
			
				mTimerHandler.postAtTime(this, mTimerNextRun);
			}
		}
	};
		
	@Override
	protected Dialog onCreateDialog(int id) {
		if (id == DIALOG_ID_AUTOLOG || id == DIALOG_ID_MANUALLOG)
		{
			//if there no asset is selected for logging, do not offer logging option
			if (mAircraftSpinner.getSelectedItemPosition() == 0 && mBatterySpinner1.getSelectedItemPosition() == 0)
			{
				return null;
			}
		
			AlertDialog.Builder builder = new AlertDialog.Builder(FlightTimerActivity.this);
		
			if (id == DIALOG_ID_AUTOLOG)
			{
				builder.setTitle("Log this flight?");
			}
			else
			{
				builder.setTitle("Add flight log?");
			}
		
			builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int id2) {
					RadioGroup gr = (RadioGroup) ((Dialog)dialog).findViewById(R.id.flightlogdialog_radio_group);
				
					if (mAircraftSpinner.getSelectedItemPosition() > 0)
					{
						Flight flight = new Flight();
						flight.setAircraft((Aircraft)mAircraftSpinner.getSelectedItem());
						flight.setDuration(mTimerCountDownInterval);
						flight.setOutcome(gr.getCheckedRadioButtonId());
						flight.save(FlightTimerActivity.this);
					}
					if (mBatterySpinner1.getSelectedItemPosition() > 0)
					{
						Cycle cycle1 = new Cycle();
						cycle1.setBattery((Battery)mBatterySpinner1.getSelectedItem());
						cycle1.setDuration(mTimerCountDownInterval);
						cycle1.setCount(1);
						cycle1.save(FlightTimerActivity.this);
					}
					if (mBatterySpinner2.getSelectedItemPosition() > 0)
					{
						Cycle cycle2 = new Cycle();
						cycle2.setBattery((Battery)mBatterySpinner2.getSelectedItem());
						cycle2.setDuration(mTimerCountDownInterval);
						cycle2.setCount(1);
						cycle2.save(FlightTimerActivity.this);
					}
					myUtils.makeToast(FlightTimerActivity.this, "Flight and/or battery cycles logged");
				}
			});
			builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int id) {
					dialog.cancel();
				}
			});
			builder.setCancelable(false);
			LinearLayout box = (LinearLayout)getLayoutInflater().inflate(R.layout.layout_flightlogdialog, null);
		    RadioGroup rg = (RadioGroup) box.findViewById(R.id.flightlogdialog_radio_group);
			  
			for (int i = 0; i < Flight.FLIGHT_OUTCOME_LABELS.length; i++)
			{
				RadioButton btn = new RadioButton(this);
				btn.setId(i);
				btn.setText(Flight.FLIGHT_OUTCOME_LABELS[i]);
				btn.setChecked((i==0));
				rg.addView(btn);
			}
			builder.setView(box);
			return builder.create();
		}
		else
			return super.onCreateDialog(id);
	}
	
	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
		if (id == DIALOG_ID_AUTOLOG || id == DIALOG_ID_MANUALLOG)
		{
			String duration = myUtils.Duration2Str(mTimerCountDownInterval, false);
			TextView msg = (TextView) dialog.findViewById(R.id.flightlogdialog_message);
			msg.setText("Flight duration: " + duration);
		}
	}
	
	private void loadDBData()
	{
		//load data from db
		Filter filter = new Filter();
		filter.is("mStatus", 1);
		
		mAircrafts = Aircraft.objects(getApplicationContext()).filter(filter).toList();
		mBatteries = Battery.objects(getApplicationContext()).filter(filter).toList();
		
		//update spinner lists
		mAircraftSpinnerList.clear();
		mAircraftSpinnerList.add(mEmptyAircraft);
		mAircraftSpinnerList.addAll(mAircrafts);
		mAircraftSpinnerAdapter.notifyDataSetChanged();
		
		mBatterySpinnerList1.clear();
		mBatterySpinnerList1.add(mEmptyBattery);
		mBatterySpinnerList1.addAll(mBatteries);
		mBatterySpinnerAdapter1.notifyDataSetChanged();
		
		mBatterySpinnerList2.clear();
		mBatterySpinnerList2.add(mEmptyBattery);
		mBatterySpinnerAdapter2.notifyDataSetChanged();
	}
	
	private void setTimePickerValue(int seconds)
	{
		int min = seconds/60;
		int secs = seconds%60;
		mTimePicker.setCurrentHour(min);
		mTimePicker.setCurrentMinute(secs);
	}
	
	private static final int TIMER_ALERT_TICK = 0;
	private static final int TIMER_ALERT_SOON = 1;
	private static final int TIMER_ALERT_DONE = 2;
	
	private void timerAlert(int alertType)
	{
		Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		final long[] pattern1 = {300,700,300,700,530,700};
		final long[] pattern2 = {300,700,300,700,300,700,300,700,300,700,300,700};
		
		switch (alertType)
		{
		case TIMER_ALERT_TICK:
			if (mVibrate.isChecked())
				v.vibrate(700);
			if (mBeep.isChecked())
			{
				mBeepCounter += 1;
				mBeepPlayer.start();
			}
			break;
		case TIMER_ALERT_SOON:
			if (mVibrate.isChecked())
				v.vibrate(pattern1, -1);
			if (mBeep.isChecked())
			{
				mBeepCounter += 3;
				mBeepPlayer.start();
			}
			break;
		case TIMER_ALERT_DONE:
			if (mVibrate.isChecked())
				v.vibrate(pattern2, -1);
			if (mBeep.isChecked())
			{
				mBeepCounter += 6;
				mBeepPlayer.start();
			}
			break;
		default:
			//nothing happens
		}
	}

	void updateUI()
	{
		if (mTimerRunning)
		{
			mTimePicker.setKeepScreenOn(true);
			mStartStop.setText("Stop");
			mAircraftSpinner.setEnabled(false);
			mBatterySpinner1.setEnabled(false);
			mBatterySpinner2.setEnabled(false);
			mBeep.setEnabled(false);
			mVibrate.setEnabled(false);	
			mLog.setEnabled(false);
		}
		else
		{
			mTimePicker.setKeepScreenOn(false);
			mStartStop.setText("Start");
			mAircraftSpinner.setEnabled(true);
			mBatterySpinner1.setEnabled(true);
			mBatterySpinner2.setEnabled(true);
			mBeep.setEnabled(true);
			mVibrate.setEnabled(true);
			mLog.setEnabled(mAircraftSpinner.getSelectedItemPosition() != 0 || mBatterySpinner1.getSelectedItemPosition() != 0);
		}
	}
	
	String buildTimerPrefKey()
	{

		//build a timer pref key of the form: TimerVal_{aircraft id}_{batt1 id}_{batt2 id}
		String pref_key = PREF_TIMER_PREFIX + "_";
		if (mAircraftSpinner.getSelectedItemPosition() != 0 && mAircraftSpinner.getSelectedItemPosition() != Spinner.INVALID_POSITION)
			pref_key += ((Aircraft) mAircraftSpinner.getSelectedItem()).getId();
		pref_key += "_";
		if (mBatterySpinner1.getSelectedItemPosition() != 0 && mBatterySpinner1.getSelectedItemPosition() != Spinner.INVALID_POSITION)
			pref_key += ((Battery) mBatterySpinner1.getSelectedItem()).getId();
		pref_key += "_";
		if (mBatterySpinner2.getSelectedItemPosition() != 0 && mBatterySpinner2.getSelectedItemPosition() != Spinner.INVALID_POSITION)
			pref_key += ((Battery) mBatterySpinner2.getSelectedItem()).getId();
		return pref_key;
	}
	
	void loadTimerPrefValue()
	{
		setTimePickerValue(mPrefs.getInt(buildTimerPrefKey(), DEFAULT_TIMER_SECS));
	}
}
